﻿namespace ClassyBlog.Infrastructure.Specs.Unit
{
    using System;
    using System.Linq;
    using System.Text.RegularExpressions;

    using Machine.Specifications;

    [Subject(typeof(MacroRunner))]
    public class when_creating_macro_runner_with_null_macros
    {
        static Exception exception;

        Because of = () => exception =
            Catch.Exception(() => new MacroRunner(null));

        It should_throw_exception = () =>
            exception.ShouldBeOfType<ArgumentNullException>();
    }

    [Subject(typeof(MacroRunner))]
    public class when_trying_to_run_macro_runner_with_unregistered_macro
    {
        const string Input = "This is a {% MyMacro %} which has not been " +
                             "registered.";

        static MacroRunner runner;
        static string output;

        Establish context = () => runner = new MacroRunner(new[]
                                                               {
                                                                   new object()
                                                               });

        Because of = () => output = runner.Run(Input);

        It should_return_the_same = () => output.ShouldEqual(Input);
    }

    [Subject(typeof(MacroRunner))]
    public class when_running_macro_runner_with_no_known_method_macro
    {
        const string Input = "This is a {% MyMacro %} which has no known " +
                             "method to run.";

        const string Output = "Unable to find any method with any of the name";

        static MacroRunner runner;
        static Exception exception;

        Establish context = () =>
            runner = new MacroRunner(new[]
                                         {
                                             new MyMacro()
                                         });

        Because of = () => exception =
            Catch.Exception(() => runner.Run(Input));

        It should_throw_exception = () =>
        {
            exception.ShouldBeOfType<InvalidOperationException>();
            exception.Message.ShouldContain(Output);
        };

        private class MyMacro
        {
        }
    }

    [Subject(typeof(MacroRunner))]
    public class when_running_macro_runner_with_incompatible_parameter_macro
    {
        const string Input = "This is a {% MyMacro 123 %} which has " +
                             "incompatible parameter type.";

        static MacroRunner runner;
        static Exception exception;

        Establish context = () =>
            runner = new MacroRunner(new[]
                                         {
                                             new MyMacro()
                                         });

        Because of = () => exception =
            Catch.Exception(() => runner.Run(Input));

        It should_throw_exception = () =>
        {
            exception.ShouldBeOfType<InvalidOperationException>();
            exception.Message.ShouldContain("Unable to convert");
        };

        private class MyMacro
        {
            public string Run(MyParameter param)
            {
                return "myMacro:" + param;
            }
        }

        private class MyParameter
        {
        }
    }

    [Subject(typeof(MacroRunner))]
    public class when_running_macro_runner
    {
        const string Content = "This is a dummy content, it is used to test " +
                               "the macro feature. If the runner ran " +
                               "successfully {% video " +
                               "http://example.com/media/video.ogg %} you " +
                               "should see all the macros are replaced with " +
                               "it's actual value. Here is one more macro " +
                               "{% gist 1193689 %} " +
                               "The above should embed the github.com gist, " +
                               "enjoy!!";

        static MacroRunner runner;
        static string result;

        Establish context = () =>
            runner = new MacroRunner(new object[]
                                         {
                                             new Video(),
                                             new Gist()
                                         });

        Because of = () => result = runner.Run(Content);

        It should_replace_content = () =>
            GetExpression().IsMatch(result).ShouldBeFalse();

        private static Regex GetExpression()
        {
            const string TypeName =
                "ClassyBlog.Infrastructure.RegexExpressions, ClassyBlog.Core";

            var type = Type.GetType(TypeName, true, false);

            var field = type.GetFields()
                            .Where(f => f.Name == "MacroMarker")
                            .Single();

            return (Regex)field.GetValue(null);
        }

        private class Video
        {
            public string Execute(string url)
            {
                return "<video>" + url + "</video>";
            }
        }

        private class Gist
        {
            public string Run(int id)
            {
                return "<gist>" + id + "</gist";
            }
        }
    }
}